#ifndef _ROSTER_H_
#define _ROSTER_H_

#include <vector>
#include <map>
#include <set>
#include <list>

#include <XMPPConnection.h>
#include <PacketListener.h>
#include <ConnectionListener.h>

class RosterPacket;
class RosterListener;
class RosterEntry;
class RosterGroup;


/**
 * Represents a user's roster, which is the collection of users a person receives
 * presence updates for. Roster items are categorized into groups for easier management.<p>
 * <p/>
 * Others users may attempt to subscribe to this user using a subscription request. Three
 * modes are supported for handling these requests: <ul>
 * <li>{@link SubscriptionMode#accept_all accept_all} -- accept all subscription requests.</li>
 * <li>{@link SubscriptionMode#reject_all reject_all} -- reject all subscription requests.</li>
 * <li>{@link SubscriptionMode#manual manual} -- manually process all subscription requests.</li>
 * </ul>
 *
 * @author Matt Tucker
 * @see XMPPConnection#getRoster()
 */
class Roster {
public:
	
	/**
     * An enumeration for the subscription mode options.
     */
	typedef enum {

        /**
         * Automatically accept all subscription and unsubscription requests. This is
         * the default mode and is suitable for simple client. More complex client will
         * likely wish to handle subscription requests manually.
         */
		ACCEPT_ALL,

        /**
         * Automatically reject all subscription requests.
         */
		REJECT_ALL,

        /**
         * Subscription requests are ignored, which means they must be manually
         * processed by registering a listener for presence packets and then looking
         * for any presence requests that have the type Presence.Type.SUBSCRIBE or
         * Presence.Type.UNSUBSCRIBE.
         */
		MANUAL
    } SubscriptionMode ;
	
    /**
     * Returns the default subscription processing mode to use when a new Roster is created. The
     * subscription processing mode dictates what action Smack will take when subscription
     * requests from other users are made. The default subscription mode
     * is {@link SubscriptionMode#accept_all}.
     *
     * @return the default subscription mode to use for new Rosters
     */
    static SubscriptionMode getDefaultSubscriptionMode() ;
	
    /**
     * Sets the default subscription processing mode to use when a new Roster is created. The
     * subscription processing mode dictates what action Smack will take when subscription
     * requests from other users are made. The default subscription mode
     * is {@link SubscriptionMode#accept_all}.
     *
     * @param subscriptionMode the default subscription mode to use for new Rosters.
     */
    static void setDefaultSubscriptionMode(SubscriptionMode subscriptionMode) ;
	
    /**
     * Creates a new roster.
     *
     * @param connection an XMPP connection.
     */
    Roster( XMPPConnection * connection) ;

    /**
     * Returns the subscription processing mode, which dictates what action
     * Smack will take when subscription requests from other users are made.
     * The default subscription mode is {@link SubscriptionMode#accept_all}.<p>
     * <p/>
     * If using the manual mode, a PacketListener should be registered that
     * listens for Presence packets that have a type of
     * {@link org.jivesoftware.smack.packet.Presence.Type#subscribe}.
     *
     * @return the subscription mode.
     */
    SubscriptionMode getSubscriptionMode() {
        return subscriptionMode;
    }
	
    /**
     * Sets the subscription processing mode, which dictates what action
     * Smack will take when subscription requests from other users are made.
     * The default subscription mode is {@link SubscriptionMode#accept_all}.<p>
     * <p/>
     * If using the manual mode, a PacketListener should be registered that
     * listens for Presence packets that have a type of
     * {@link org.jivesoftware.smack.packet.Presence.Type#subscribe}.
     *
     * @param subscriptionMode the subscription mode.
     */
    void setSubscriptionMode(SubscriptionMode & subscriptionMode) {
        this->subscriptionMode = subscriptionMode;
    }

    /**
     * Reloads the entire roster from the server. This is an asynchronous operation,
     * which means the method will return immediately, and the roster will be
     * reloaded at a later point when the server responds to the reload request.
     */
    void reload();

    /**
     * Adds a listener to this roster. The listener will be fired anytime one or more
     * changes to the roster are pushed from the server.
     *
     * @param rosterListener a roster listener.
     */
    void addRosterListener(RosterListener * rosterListener) ;
	
    /**
     * Removes a listener from this roster. The listener will be fired anytime one or more
     * changes to the roster are pushed from the server.
     *
     * @param rosterListener a roster listener.
     */
    void removeRosterListener(RosterListener * rosterListener) ;
	
    /**
     * Creates a new group.<p>
     * <p/>
     * Note: you must add at least one entry to the group for the group to be kept
     * after a logout/login. This is due to the way that XMPP stores group information.
     *
     * @param name the name of the group.
     * @return a new group.
     */
    RosterGroup * createGroup(std::string & name) ;
	
    /**
     * Creates a new roster entry and presence subscription. The server will asynchronously
     * update the roster with the subscription status.
     *
     * @param user   the user. (e.g. johndoe@jabber.org)
     * @param name   the nickname of the user.
     * @param groups the list of group names the entry will belong to, or <tt>null</tt> if the
     *               the roster entry won't belong to a group.
     * @throws XMPPException if an XMPP exception occurs.
     */
    void createEntry(std::string & user, std::string & name, std::vector<std::string> & groups) ;

    /**
     * Removes a roster entry from the roster. The roster entry will also be removed from the
     * unfiled entries or from any roster group where it could belong and will no longer be part
     * of the roster. Note that this is an asynchronous call -- Smack must wait for the server
     * to send an updated subscription status.
     *
     * @param entry a roster entry.
     * @throws XMPPException if an XMPP error occurs.
     */
    void removeEntry(RosterEntry * entry) ;
	
    /**
     * Returns a count of the entries in the roster.
     *
     * @return the number of entries in the roster.
     */
    int getEntryCount() {
        return getEntries().size();
    }

    /**
     * Returns an unmodifiable collection of all entries in the roster, including entries
     * that don't belong to any groups.
     *
     * @return all entries in the roster.
     */
	std::list <RosterEntry*> getEntries(); 

    /**
     * Returns a count of the unfiled entries in the roster. An unfiled entry is
     * an entry that doesn't belong to any groups.
     *
     * @return the number of unfiled entries in the roster.
     */
    int getUnfiledEntryCount() {
        return unfiledEntries.size();
    }
	
    /**
     * Returns an unmodifiable collection for the unfiled roster entries. An unfiled entry is
     * an entry that doesn't belong to any groups.
     *
     * @return the unfiled roster entries.
     */
	std::list<RosterEntry*> getUnfiledEntries() {
        return unfiledEntries;
    }

    /**
     * Returns the roster entry associated with the given XMPP address or
     * <tt>null</tt> if the user is not an entry in the roster.
     *
     * @param user the XMPP address of the user (eg "jsmith@example.com"). The address could be
     *             in any valid format (e.g. "domain/resource", "user@domain" or "user@domain/resource").
     * @return the roster entry or <tt>null</tt> if it does not exist.
     */
    RosterEntry * getEntry(std::string & user) ;

    /**
     * Returns true if the specified XMPP address is an entry in the roster.
     *
     * @param user the XMPP address of the user (eg "jsmith@example.com"). The
     *             address could be in any valid format (e.g. "domain/resource",
     *             "user@domain" or "user@domain/resource").
     * @return true if the XMPP address is an entry in the roster.
     */
    bool contains(std::string & user) {
		//TODO improve performance .
        return getEntry(user) != NULL;
    }	
	
    /**
     * Returns the roster group with the specified name, or <tt>null</tt> if the
     * group doesn't exist.
     *
     * @param name the name of the group.
     * @return the roster group with the specified name.
     */
    RosterGroup * getGroup(std::string & name) {
        return groups[name];
    }

    /**
     * Returns the number of the groups in the roster.
     *
     * @return the number of groups in the roster.
     */
    int getGroupCount() {
        return groups.size();
    }

    /**
     * Returns an unmodiable collections of all the roster groups.
     *
     * @return an iterator for all roster groups.
     */
	std::list<RosterGroup*> getGroups();

    /**
     * Returns the presence info for a particular user. If the user is offline, or
     * if no presence data is available (such as when you are not subscribed to the
     * user's presence updates), unavailable presence will be returned.<p>
     * <p/>
     * If the user has several presences (one for each resource), then the presence with
     * highest priority will be returned. If multiple presences have the same priority,
     * the one with the "most available" presence mode will be returned. In order,
     * that's {@link Presence.Mode#chat free to chat}, {@link Presence.Mode#available available},
     * {@link Presence.Mode#away away}, {@link Presence.Mode#xa extended away}, and
     * {@link Presence.Mode#dnd do not disturb}.<p>
     * <p/>
     * Note that presence information is received asynchronously. So, just after logging
     * in to the server, presence values for users in the roster may be unavailable
     * even if they are actually online. In other words, the value returned by this
     * method should only be treated as a snapshot in time, and may not accurately reflect
     * other user's presence instant by instant. If you need to track presence over time,
     * such as when showing a visual representation of the roster, consider using a
     * {@link RosterListener}.
     *
     * @param user an XMPP ID. The address could be in any valid format (e.g.
     *             "domain/resource", "user@domain" or "user@domain/resource"). Any resource
     *             information that's part of the ID will be discarded.
     * @return the user's current presence, or unavailable presence if the user is offline
     *         or if no presence information is available..
     */
    Presence * getPresence(std::string & user) ;
	
    /**
     * Returns the presence info for a particular user's resource, or unavailable presence
     * if the user is offline or if no presence information is available, such as


     * when you are not subscribed to the user's presence updates.
     *
     * @param userWithResource a fully qualified XMPP ID including a resource (user@domain/resource).
     * @return the user's current presence, or unavailable presence if the user is offline
     *         or if no presence information is available.
     */
    Presence * getPresenceResource(std::string & userWithResource) ;
	
    /**
     * Returns an iterator (of Presence objects) for all of a user's current presences
     * or an unavailable presence if the user is unavailable (offline) or if no presence
     * information is available, such as when you are not subscribed to the user's presence
     * updates.
     *
     * @param user a XMPP ID, e.g. jdoe@example.com.
     * @return an iterator (of Presence objects) for all the user's current presences,
     *         or an unavailable presence if the user is offline or if no presence information
     *         is available.
     */
	//TODO
    //Iterator<Presence*> getPresences(std::string & user) ;
	
    /**
     * Cleans up all resources used by the roster.
     */
    void cleanup() { // havn't free the memory!!!
        rosterListeners.clear();
    }

	bool isInitialized() { return rosterInitialized;}
	
private:

    /**
     * Listens for all presence packets and processes them.
     */
    class PresencePacketListener : public PacketListener {
	public:
		void processPacket( Packet * packet);
	};
	
	/**
     * Listens for all roster packets and processes them.
     */
    class RosterPacketListener : public PacketListener {
	public:
		void processPacket( Packet * packet);
	};

	class OfflineConnectionListener : public ConnectionListener {
	public:
		OfflineConnectionListener(Roster * r) : r(r) {}
		void connectionClosed() { r->setOfflinePresences(); }
		void connectionClosedOnError( std::string & err/* Exception e */) { r->setOfflinePresences(); }
		void reconnectingIn(int seconds) {}
		void reconnectionFailed(/*Exception e*/) {}
		void reconnectionSuccessful() {}
	private:
		Roster * r;
	};

    /**
     * Returns the key to use in the presenceMap for a fully qualified XMPP ID.
     * The roster can contain any valid address format such us "domain/resource",
     * "user@domain" or "user@domain/resource". If the roster contains an entry
     * associated with the fully qualified XMPP ID then use the fully qualified XMPP
     * ID as the key in presenceMap, otherwise use the bare address. Note: When the
     * key in presenceMap is a fully qualified XMPP ID, the userPresences is useless
     * since it will always contain one entry for the user.
     *
     * @param user the bare or fully qualified XMPP ID, e.g. jdoe@example.com or
     *             jdoe@example.com/Work.
     * @return the key to use in the presenceMap for the fully qualified XMPP ID.
     */
	std::string getPresenceMapKey(std::string user);

    /**
     * Changes the presence of available contacts offline by simulating an unavailable
     * presence sent from the server. After a disconnection, every Presence is set
     * to offline.
     */
    void setOfflinePresences() ;

    /**
     * Fires roster changed event to roster listeners indicating that the
     * specified collections of contacts have been added, updated or deleted
     * from the roster.
     *
     * @param addedEntries   the collection of address of the added contacts.
     * @param updatedEntries the collection of address of the updated contacts.
     * @param deletedEntries the collection of address of the deleted contacts.
     */
    void fireRosterChangedEvent(std::vector<std::string> & addedEntries, std::vector<std::string> & updatedEntries,
            std::vector<std::string> & deletedEntries) ;
	
    /**
     * Fires roster presence changed event to roster listeners.
     *
     * @param presence the presence change.
     */
    void fireRosterPresenceEvent(Presence * presence) ;
	

    /**
     * The default subscription processing mode to use when a Roster is created. By default
     * all subscription requests are automatically accepted.
     */
	static SubscriptionMode defaultSubscriptionMode;
	
	XMPPConnection * connection;
	std::map<std::string, RosterGroup*> groups;
	std::vector<RosterEntry*> entries;
	std::list<RosterEntry*> unfiledEntries;
	std::set<RosterListener*> rosterListeners;
	std::map<std::string, std::map<std::string, Presence*> > presenceMap;
    // The roster is marked as initialized when at least a single roster packet
    // has been recieved and processed.
    bool rosterInitialized ;
    PresencePacketListener * presencePacketListener;

    SubscriptionMode subscriptionMode ;

};

#endif // -- _ROSTER_H_
